﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Collections.Generic;

namespace MaterialEditor
{
    /// <summary>
    /// A texutre that changes each time it's rendered
    /// </summary>
    public class DynamicTexture
    {
        /// <summary>
        /// The base color data used by the dyanamic texture to modify
        /// </summary>
        public Texture2D Material;

        public TextureEffects effects = new TextureEffects();

        private RenderTarget2D renderTgt;

        private RenderTarget2D otherRenderTgt;

        private bool _disposed;

        /// <summary>
        /// The modified texture
        /// </summary>
        public Texture2D Texture
        {
            get { return renderTgt as Texture2D; }
        }

        /// <summary>
        /// Creates a new instance of DynamicTexture
        /// </summary>
        /// <param name="texture">The material for the dynamic texture</param>
        public DynamicTexture(Texture2D texture)
        {
            this.Material = texture;

            renderTgt = new RenderTarget2D(Graphics.Instance.device, texture.Width, texture.Height);
            otherRenderTgt = new RenderTarget2D(Graphics.Instance.device, texture.Width, texture.Height);

            instances.Add(this);
        }

        /// <summary>
        /// Destroys this instance of DynamicTexture
        /// </summary>
        public void Dispose()
        {
            _disposed = true;
        }

        /// <summary>
        /// Renders the texture to the screen and updates it in the process
        /// </summary>
        /// <param name="position">The location to render the updated texture</param>
        public void Draw(Vector2 position, Vector2 size, Vector2[] texCoords)
        {
            Graphics g = Graphics.Instance;

            g.DrawTextureRectWithTexcoords(new System.Drawing.Rectangle((int)position.X, (int)position.Y, (int)size.X, (int)size.Y), (Texture2D)renderTgt, null);
        }

        public static implicit operator DynamicTexture(Texture2D texture)
        {
            return new DynamicTexture(texture);
        }

        public static List<DynamicTexture> instances = new List<DynamicTexture>();

        /// <summary>
        /// Renders the provided material to the provided render target
        /// </summary>
        /// <param name="effect">The shader to render the material</param>
        /// <param name="renderTgt">The render target to render to</param>
        /// <param name="material">The material to render</param>
        private static void DrawToRT(Effect effect, RenderTarget2D renderTgt, Texture2D material, Vector2[] texCoords)
        {
            Graphics g = Graphics.Instance;
            if (effect != null)
            {
                effect.Parameters["World"].SetValue(Matrix.CreateScale(material.Width, material.Height, 0.0f));
                effect.Parameters["View"].SetValue(Matrix.CreateLookAt(Vector3.Backward, Vector3.Zero, Vector3.Up));
                effect.Parameters["Projection"].SetValue(Matrix.CreateOrthographicOffCenter(0, material.Width, material.Height, 0, 0, 1));
            }

            g.device.SetRenderTarget(renderTgt); 
            g.device.Clear(Color.Black);
            g.effect = effect;
            g.DrawTextureRectWithTexcoords(new System.Drawing.Rectangle(0, 0, material.Width, material.Height), material, texCoords);
            g.device.SetRenderTarget(null);
            g.effect = null;
        }

        /// <summary>
        /// Updates all dynamic textures
        /// </summary>
        public static void Update()
        {
            Graphics g = Graphics.Instance;

            for (int i = 0; i < instances.Count; i++)
            {
                if (instances[i]._disposed)
                {
                    instances.RemoveAt(i);
                    i--;
                    continue;
                }
                if (instances[i].effects.Count == 0)
                    DrawToRT(EffectManager.BasicShader, instances[i].renderTgt, instances[i].Material, null);
                for (int ii = 0; ii < instances[i].effects.Count; ii++)
                {
                    Effect effect = instances[i].effects[ii].effect;
                    if (effect != null)
                    {
                        if (instances[i].effects[ii].textures != null)
                        {
                            int index = 1;
                            foreach (Texture2D tex in instances[i].effects[ii].textures)
                            {
                                g.device.Textures[index] = tex;
                                index++;
                            }
                        }
                        DrawToRT(effect, instances[i].otherRenderTgt, instances[i].renderTgt, instances[i].effects.TexCoords);
                        DrawToRT(EffectManager.BasicShader, instances[i].renderTgt, instances[i].otherRenderTgt, null);
                    }
                    else
                        DrawToRT(EffectManager.BasicShader, instances[i].renderTgt, instances[i].Material, instances[i].effects.TexCoords);
                }
            }
        }
    }
}